/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.google.code.twiddling.core.command.builtin;

import java.io.PrintWriter;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.OperatingSystemMXBean;
import java.lang.management.RuntimeMXBean;
import java.lang.management.ThreadMXBean;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;

import com.google.code.twiddling.core.command.AbstractCommand;
import com.google.code.twiddling.core.command.Command;
import com.google.code.twiddling.core.command.CommandContext;
import com.google.code.twiddling.core.io.text.TextObjectFactory;
import com.google.code.twiddling.core.io.text.TextTable;


/**
 * 
 * @author <a href="mailto:jeff.yuchang@gmail.com">Jeff Yu</a>
 *
 */
@Command(name="info", description="show the JVM information",
		usage="info [OPTION]...")
public class InfoCommand extends AbstractCommand {
	
    private NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
    private NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));

	@Override
	public void doExecute(CommandContext context) throws Exception {
		PrintWriter out = context.getIO().getOut();
		
        RuntimeMXBean         runtime = ManagementFactory.getRuntimeMXBean();
        OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
        ThreadMXBean          threads = ManagementFactory.getThreadMXBean();
        MemoryMXBean          mem = ManagementFactory.getMemoryMXBean();
        ClassLoadingMXBean    cl = ManagementFactory.getClassLoadingMXBean();
        
		out.println("JVM");		
		TextTable jvmTable = TextObjectFactory.getTable("default");
		jvmTable.getSchema().setShowHeaders(false);
		jvmTable.getSchema().setColumnSep("");
		jvmTable.addColumns(new String[] {"key", "value"});
		jvmTable.setColumnWidth("key", 25);
		jvmTable.addRow(new String[]{"Java Virtual Machine :", runtime.getVmName()+" version " + runtime.getVmVersion()});
		jvmTable.addRow(new String[]{"Vendor :", runtime.getVmVendor()});
		jvmTable.addRow(new String[]{"Uptime :", printDuration(runtime.getUptime())});
		jvmTable.print(out);
		
		
		out.println("");
		out.println("Threads");
		TextTable threadTable = TextObjectFactory.getTable("default");
		threadTable.getSchema().setShowHeaders(false);
		threadTable.getSchema().setColumnSep("");
		threadTable.addColumns(new String[] {"key", "value"});
		threadTable.setColumnWidth("key", 25);
		threadTable.addRow(new String[]{"Live threads :", Integer.toString(threads.getThreadCount())});
		threadTable.addRow(new String[]{"Daemon threads :", Integer.toString(threads.getDaemonThreadCount())});
		threadTable.addRow(new String[]{"Peak :", Integer.toString(threads.getPeakThreadCount())});
		threadTable.addRow(new String[]{"Total started :", Long.toString(threads.getTotalStartedThreadCount())});
		threadTable.print(out);
		
		out.println("");
        out.println("Memory");        
		TextTable memTable = TextObjectFactory.getTable("default");
		memTable.getSchema().setShowHeaders(false);
		memTable.getSchema().setColumnSep("");
		memTable.addColumns(new String[] {"key", "value"});
		memTable.setColumnWidth("key", 25);
		memTable.addRow(new String[]{"Current heap size :", printSizeInKb(mem.getHeapMemoryUsage().getUsed())});
		memTable.addRow(new String[]{"Maximum heap size :", printSizeInKb(mem.getHeapMemoryUsage().getMax())});
		memTable.addRow(new String[]{"Committed heap size :", printSizeInKb(mem.getHeapMemoryUsage().getCommitted())});
		memTable.addRow(new String[]{"Pending objects :", Integer.toString(mem.getObjectPendingFinalizationCount())});
		memTable.print(out);
		
		out.println("");
        out.println("Classes");
		TextTable clzTable = TextObjectFactory.getTable("default");
		clzTable.getSchema().setShowHeaders(false);
		clzTable.getSchema().setColumnSep("");
		clzTable.addColumns(new String[] {"key", "value"});
		clzTable.setColumnWidth("key", 25);
		clzTable.addRow(new String[]{"Current classes loaded :", printLong(cl.getLoadedClassCount())});
		clzTable.addRow(new String[]{"Total classes loaded :", printLong(cl.getTotalLoadedClassCount())});
		clzTable.addRow(new String[]{"Total classes unloaded :", printLong(cl.getUnloadedClassCount())});
		clzTable.print(out);
		
		out.println("");
        out.println("Operating System");
		TextTable osTable = TextObjectFactory.getTable("default");
		osTable.getSchema().setShowHeaders(false);
		osTable.getSchema().setColumnSep("");
		osTable.addColumns(new String[] {"key", "value"});
		osTable.setColumnWidth("key", 25);
		osTable.addRow(new String[]{"Name :", os.getName() + " version " + os.getVersion()});
		osTable.addRow(new String[]{"Architecture :", os.getArch()});
		osTable.addRow(new String[]{"Processors :", Integer.toString(os.getAvailableProcessors())});
		osTable.print(out);
		
        		
	}

    private String printSizeInKb(double size) {
        return fmtI.format((long) (size / 1024)) + " kbytes";
    }
    
    private String printLong(long i) {
        return fmtI.format(i);
    }
	
    private String printDuration(double uptime) {
        uptime /= 1000;
        if (uptime < 60) {
            return fmtD.format(uptime) + " seconds";
        }
        uptime /= 60;
        if (uptime < 60) {
            long minutes = (long) uptime;
            String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
            return s;
        }
        uptime /= 60;
        if (uptime < 24) {
            long hours = (long) uptime;
            long minutes = (long) ((uptime - hours) * 60);
            String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
            if (minutes != 0) {
                s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : "minute");
            }
            return s;
        }
        uptime /= 24;
        long days = (long) uptime;
        long hours = (long) ((uptime - days) * 60);
        String s = fmtI.format(days) + (days > 1 ? " days" : " day");
        if (hours != 0) {
            s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : "hour");
        }
        return s;
    }
	
	
}
